package org.mentalsmash.crossroads;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.List;

import org.apache.log4j.Logger;
import org.mentalsmash.crossroads.Crossword.CrosswordEntry;

public class XhtmlExporter implements CrosswordExporter<String> {

	private final static Logger log = Logger.getLogger(XhtmlExporter.class);
	
	private final static String TITLE_ID = "title";
	private final static String DEFINITIONS_ID = "defintions";
	private final static String ACROSS_DEFINITIONS_ID = "acrossDefinitions";
	private final static String DOWN_DEFINITIONS_ID = "downDefinitions";
	private final static String SVG_ID = "svgContainer";
	
	private final static String ACR_DWN_CLASS = "acrossDown";
	private final static String DEFINITION_CLASS = "definition";
	private final static String DEFINITION_LIST_CLASS = "definitionList";
	private final static String DEFNUM_CLASS = "defNumber";
	
	
	private boolean _withDefs = true;
	
	public XhtmlExporter(){
	};
	
	public XhtmlExporter(boolean withDefs){
		setWithDefs(withDefs);
	}
	
	public void setWithDefs(boolean withDefs) {
		_withDefs = withDefs;
	}

	public boolean isWithDefs() {
		return _withDefs;
	}

	@Override
	public String toExternalRepresentation(Crossword input) {
		ByteArrayOutputStream outputString = new ByteArrayOutputStream();
		buildXhtmlDocument(input,outputString);
		String built = null;
		try{
			built = new String(outputString.toByteArray(),Configuration.getInstance().getEncoding());
		} catch (UnsupportedEncodingException e ){
			log.fatal("Unsupported encoding while writing svg: " + Configuration.getInstance().getEncoding());
			e.printStackTrace();
			System.exit(-1);
		} finally {
			try{ 
			 outputString.close();
			} catch (IOException e) {
				log.warn("While closing SVG outputstream: "+ e.getMessage()); 
			}
		}
		
		return built;
	}

	public void buildXhtmlDocument(Crossword input,
			OutputStream outputString) {
		
		PrintStream outXml = new PrintStream(outputString);
		log.debug("Generating xhtml representation...");
		
		outXml.append("<?xml version='1.0' encoding='UTF-8'?>\n");
//		outXml.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\" [\n" +
//				"%html;\n" +
//				"<!ENTITY % svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\" >\n" +
//				"%svg;\n" +
//				   "] >\n");
		outXml.append("<!DOCTYPE html PUBLIC " + 
				"\"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" " +
				"\"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n");
		outXml.append("<html xmlns='http://www.w3.org/1999/xhtml' " +
				"xml:lang='" + Configuration.getInstance().getLanguage() + "' >\n");
		
		printHeadElement(input,outXml);
		printBodyElement(input,outXml);
		outXml.append("</html>\n");
		log.debug("Done.");
		

		
	}

	private void printStyleElement(PrintStream outXml) {
		outXml.println("<style type='text/css'>");
//		outXml.println("<!--");
		outXml.print(
			  "body {\n" +
			  "    font-family:arial,sans-serif;\n}" +
			  "div#" + TITLE_ID +  " > h1 {\n" +
			  "    font-size: 1.7em; \n" +
			  "    text-align: left;" +
			  "    margin: 0.8em 0 0.2em " + SVGExporter.CELL_WIDTH +"px;}\n" +
			  "div#" + SVG_ID + " {\n" +
			  "    margin: 0 auto 0 auto;" +
			  "    padding: 0 0 0 0;}\n" +
			  "div#" + DEFINITIONS_ID + " {\n" +
			  "    margin-left: " + SVGExporter.CELL_WIDTH/2 + "px;}\n" + 
			  "div#" + ACROSS_DEFINITIONS_ID + " {\n" +
			  "    float:left;\n" +
			  "    margin-right: "+ SVGExporter.CELL_WIDTH + "px;}\n" + 
			  "div#" + DOWN_DEFINITIONS_ID + " {\n" + 
			  "    float:left;}\n" +
			  "p." + ACR_DWN_CLASS + " {\n" +
			  "    font-family:arial,sans-serif;\n" +
			  "    font-weight: bold;\n" +
			  "    font-size: 1em;}\n" +
			  "dl."+DEFINITION_LIST_CLASS+ " {\n" +
			  "    font-family: arial,sans-serif;\n" +
			  "    fond-size: 2em;}\n"
			+ "dt."+DEFNUM_CLASS+" {\n" 
			+ "    float: left;\n"
			+ "    padding-right: 0.5em;\n"
			+ "    font-weight: bold;}\n"
			+ "dd."+DEFINITION_CLASS+" {\n"
			+ "    margin: 0.1em 0 ;}\n"
		);
//		outXml.println("-->");
		outXml.println("</style>");
		
	}

	private void printBodyElement(Crossword input, PrintStream outXml) {
		outXml.println("<body>");
		printTitleDiv(input, outXml);
		printEmbeddedSvg(input,outXml);
		if(isWithDefs())
			printDefintionsDiv(input,outXml);
		outXml.println("</body>");
		
	}

	private void printDefintionsDiv(Crossword input, PrintStream outXml) {
		List<CrosswordEntry> across = input.getOrderedAcrossEntries();
		List<CrosswordEntry> down = input.getOrderedDownEntries();
		
		outXml.println("<div id='"+ DEFINITIONS_ID +"' >");
		
		outXml.println("<div id='"+ ACROSS_DEFINITIONS_ID +"' >");
		outXml.println("<p class='"+ACR_DWN_CLASS+"' >"+ "Across:" + "</p>");
		printDefinitions(across,outXml);
		outXml.println("</div>");
		
		outXml.println("<div id='"+ DOWN_DEFINITIONS_ID +"' >");
		outXml.println("<p class='"+ACR_DWN_CLASS+"' >"+ "Down:" + "</p>");
		printDefinitions(down,outXml);
		outXml.println("</div>");
		outXml.println("</div>");
		
	}

	private void printDefinitions(List<CrosswordEntry> defintions,
			PrintStream outXml) {
		
		outXml.println("<dl class='" + DEFINITION_LIST_CLASS + "' >");
		for(CrosswordEntry current : defintions){
			outXml.print("<dt class='" + DEFNUM_CLASS + "' >" + current.getFriendlyNumber() + "." + "</dt>");
			outXml.print("<dd class='" + DEFINITION_CLASS +"' >"+ current.getDefinition() + "</dd>\n");
		} 
		outXml.println("</dl>");
	}

	private void printEmbeddedSvg(Crossword input, PrintStream outXml) {
		SVGExporter exporter = new SVGExporter(false,false);
		outXml.println("<div id='"+ SVG_ID + "' >");
		exporter.buildSvgDocument(input, outXml);
		outXml.println("</div>");
	}

	private void printTitleDiv(Crossword input, PrintStream outXml) {
		outXml.println("<div id='" + TITLE_ID +"'>");
		outXml.println("<h1>Crossword " + input.getCrosswordId() + "</h1>");
		outXml.println("</div>");
	}

	private void printHeadElement(Crossword input, PrintStream outXml) {
		outXml.println("<head>");
		outXml.println("<title>Crossword " + input.getCrosswordId() + "</title>");
		printStyleElement(outXml);
		outXml.println("</head>");
		
	}

}
